/*
 * Copyright (c) 2025, Intel Corporation. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

import java.util.Arrays;
import java.util.Random;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.reflect.Method;
import java.util.HexFormat;

/*
 * @test
 * @requires os.simpleArch == "x64"
 * @library /test/lib
 * @key randomness
 * @modules java.base/sun.security.provider:+open
 * @run main/othervm -XX:UseAVX=2 ML_DSA_Intrinsic_Test
 */
/*
 * @test
 * @comment This test should be reenabled on aarch64
 * @requires os.simpleArch == "x64"
 * @library /test/lib
 * @key randomness
 * @modules java.base/sun.security.provider:+open
 * @run main ML_DSA_Intrinsic_Test
 */

// To run manually: java --add-opens java.base/sun.security.provider=ALL-UNNAMED --add-exports java.base/sun.security.provider=ALL-UNNAMED
//  -XX:+UnlockDiagnosticVMOptions -XX:+UseDilithiumIntrinsics test/jdk/sun/security/provider/acvp/ML_DSA_Intrinsic_Test.java

public class ML_DSA_Intrinsic_Test {
    public static void main(String[] args) throws Throwable {
        MethodHandles.Lookup lookup = MethodHandles.lookup();
        Class<?> kClazz = sun.security.provider.ML_DSA.class;

        Method m = kClazz.getDeclaredMethod("implDilithiumNttMult",
                int[].class, int[].class, int[].class);
        m.setAccessible(true);
        MethodHandle mult = lookup.unreflect(m);

        m = kClazz.getDeclaredMethod("implDilithiumNttMultJava",
                int[].class, int[].class, int[].class);
        m.setAccessible(true);
        MethodHandle multJava = lookup.unreflect(m);

        m = kClazz.getDeclaredMethod("implDilithiumMontMulByConstant",
                int[].class, int.class);
        m.setAccessible(true);
        MethodHandle multConst = lookup.unreflect(m);

        m = kClazz.getDeclaredMethod("implDilithiumMontMulByConstantJava",
                int[].class, int.class);
        m.setAccessible(true);
        MethodHandle multConstJava = lookup.unreflect(m);

        m = kClazz.getDeclaredMethod("implDilithiumDecomposePoly",
                int[].class, int[].class, int[].class, int.class, int.class);
        m.setAccessible(true);
        MethodHandle decompose = lookup.unreflect(m);

        m = kClazz.getDeclaredMethod("decomposePolyJava",
                int[].class, int[].class, int[].class, int.class, int.class);
        m.setAccessible(true);
        MethodHandle decomposeJava = lookup.unreflect(m);

        m = kClazz.getDeclaredMethod("implDilithiumAlmostNtt",
        int[].class, int[].class);
        m.setAccessible(true);
        MethodHandle almostNtt = lookup.unreflect(m);

        m = kClazz.getDeclaredMethod("implDilithiumAlmostNttJava",
                int[].class);
        m.setAccessible(true);
        MethodHandle almostNttJava = lookup.unreflect(m);

        m = kClazz.getDeclaredMethod("implDilithiumAlmostInverseNtt",
        int[].class, int[].class);
        m.setAccessible(true);
        MethodHandle inverseNtt = lookup.unreflect(m);

        m = kClazz.getDeclaredMethod("implDilithiumAlmostInverseNttJava",
                int[].class);
        m.setAccessible(true);
        MethodHandle inverseNttJava = lookup.unreflect(m);

        // Hint: if test fails, you can hardcode the seed to make the test more reproducible
        Random rnd = new Random();
        long seed = rnd.nextLong();
        rnd.setSeed(seed);
        //Note: it might be useful to increase this number during development of new intrinsics
        final int repeat = 10000;
        int[] coeffs1 = new int[ML_DSA_N];
        int[] coeffs2 = new int[ML_DSA_N];
        int[] prod1 = new int[ML_DSA_N];
        int[] prod2 = new int[ML_DSA_N];
        int[] prod3 = new int[ML_DSA_N];
        int[] prod4 = new int[ML_DSA_N];
        for (int i = 0; i < repeat; i++) {
            // Hint: if test fails, you can hardcode the seed to make the test more reproducible:
            // rnd.setSeed(seed);
            testMult(prod1, prod2, coeffs1, coeffs2, mult, multJava, rnd, seed, i);
            testMultConst(prod1, prod2, multConst, multConstJava, rnd, seed, i);
            testDecompose(prod1, prod2, prod3, prod4, coeffs1, coeffs2, decompose, decomposeJava, rnd, seed, i);
            testAlmostNtt(coeffs1, coeffs2, almostNtt, almostNttJava, rnd, seed, i);
            testInverseNtt(coeffs1, coeffs2, inverseNtt, inverseNttJava, rnd, seed, i);
        }
        System.out.println("Fuzz Success");
    }

    private static final int ML_DSA_N = 256;
    public static void testMult(int[] prod1, int[] prod2, int[] coeffs1, int[] coeffs2,
        MethodHandle mult, MethodHandle multJava, Random rnd,
        long seed, int i) throws Throwable {

        for (int j = 0; j<ML_DSA_N; j++) {
            coeffs1[j] = rnd.nextInt();
            coeffs2[j] = rnd.nextInt();
        }

        mult.invoke(prod1, coeffs1, coeffs2);
        multJava.invoke(prod2, coeffs1, coeffs2);

        if (!Arrays.equals(prod1, prod2)) {
                throw new RuntimeException("[Seed "+seed+"@"+i+"] Result mult mismatch: " + formatOf(prod1) + " != " + formatOf(prod2));
        }
    }

    public static void testMultConst(int[] prod1, int[] prod2,
        MethodHandle multConst, MethodHandle multConstJava, Random rnd,
        long seed, int i) throws Throwable {

        for (int j = 0; j<ML_DSA_N; j++) {
            prod1[j] = prod2[j] = rnd.nextInt();
        }
        // Per Algorithm 3 in https://eprint.iacr.org/2018/039.pdf, one of the inputs is bound, which prevents overflows
        int dilithium_q = 8380417;
        int c = rnd.nextInt(dilithium_q);

        multConst.invoke(prod1, c);
        multConstJava.invoke(prod2, c);

        if (!Arrays.equals(prod1, prod2)) {
                throw new RuntimeException("[Seed "+seed+"@"+i+"] Result multConst mismatch: " + formatOf(prod1) + " != " + formatOf(prod2));
        }
    }

    public static void testDecompose(int[] low1, int[] high1, int[] low2, int[] high2, int[] coeffs1, int[] coeffs2,
        MethodHandle decompose, MethodHandle decomposeJava, Random rnd,
        long seed, int i) throws Throwable {

        for (int j = 0; j<ML_DSA_N; j++) {
            coeffs1[j] = coeffs2[j] = rnd.nextInt();
        }
        int gamma2 = 95232;
        if (rnd.nextBoolean()) {
            gamma2 = rnd.nextInt();
        }
        int multiplier = (gamma2 == 95232 ? 22 : 8);

        decompose.invoke(coeffs1, low1, high1, 2 * gamma2, multiplier);
        decomposeJava.invoke(coeffs2, low2, high2, 2 * gamma2, multiplier);

        if (!Arrays.equals(low1, low2)) {
                throw new RuntimeException("[Seed "+seed+"@"+i+"] Result low mismatch: " + formatOf(low1) + " != " + formatOf(low2));
        }

        if (!Arrays.equals(high1, high2)) {
                throw new RuntimeException("[Seed "+seed+"@"+i+"] Result high mismatch: " + formatOf(high1) + " != " + formatOf(high2));
        }
    }

    public static void testAlmostNtt(int[] coeffs1, int[] coeffs2,
        MethodHandle almostNtt, MethodHandle almostNttJava, Random rnd,
        long seed, int i) throws Throwable {
        for (int j = 0; j<ML_DSA_N; j++) {
            coeffs1[j] = coeffs2[j] = rnd.nextInt();
        }

        almostNtt.invoke(coeffs1, MONT_ZETAS_FOR_VECTOR_NTT);
        almostNttJava.invoke(coeffs2);

        if (!Arrays.equals(coeffs1, coeffs2)) {
            throw new RuntimeException("[Seed "+seed+"@"+i+"] Result AlmostNtt mismatch: " + formatOf(coeffs1) + " != " + formatOf(coeffs2));
        }
    }

    public static void testInverseNtt(int[] coeffs1, int[] coeffs2,
        MethodHandle inverseNtt, MethodHandle inverseNttJava, Random rnd,
        long seed, int i) throws Throwable {
        for (int j = 0; j<ML_DSA_N; j++) {
            coeffs1[j] = coeffs2[j] = rnd.nextInt();
        }

        inverseNtt.invoke(coeffs1, MONT_ZETAS_FOR_VECTOR_INVERSE_NTT);
        inverseNttJava.invoke(coeffs2);

        if (!Arrays.equals(coeffs1, coeffs2)) {
            throw new RuntimeException("[Seed "+seed+"@"+i+"] Result InverseNtt mismatch: " + formatOf(coeffs1) + " != " + formatOf(coeffs2));
        }
    }

    private static CharSequence formatOf(int[] arr) {
        StringBuilder b = new StringBuilder(arr.length*8);
        HexFormat hex = HexFormat.of();
        for (int j = 0; j<arr.length; j++) {
            b.append(hex.toHexDigits(arr[j]));
        }
        return b.toString();
    }

    // Copied constants from sun.security.provider.ML_DSA
    private static final int[] MONT_ZETAS_FOR_VECTOR_INVERSE_NTT = new int[]{
            -1976782, 846154, -1400424, -3937738, 1362209, 48306, -3919660, 554416,
            3545687, -1612842, 976891, -183443, 2286327, 420899, 2235985, 2939036,
            3833893, 260646, 1104333, 1667432, -1910376, 1803090, -1723600, 426683,
            -472078, -1717735, 975884, -2213111, -269760, -3866901, -3523897, 3038916,
            1799107, 3694233, -1652634, -810149, -3014001, -1616392, -162844, 3183426,
            1207385, -185531, -3369112, -1957272, 164721, -2454455, -2432395, 2013608,
            3776993, -594136, 3724270, 2584293, 1846953, 1671176, 2831860, 542412,
            -3406031, -2235880, -777191, -1500165, 1374803, 2546312, -1917081, 1279661,
            1962642, -3306115, -1312455, 451100, 1430225, 3318210, -1237275, 1333058,
            1050970, -1903435, -1869119, 2994039, 3548272, -2635921, -1250494, 3767016,
            -1595974, -2486353, -1247620, -4055324, -1265009, 2590150, -2691481, -2842341,
            -203044, -1735879, 3342277, -3437287, -4108315, 2437823, -286988, -342297,
            3595838, 768622, 525098, 3556995, -3207046, -2031748, 3122442, 655327,
            522500, 43260, 1613174, -495491, -819034, -909542, -1859098, -900702,
            3193378, 1197226, 3759364, 3520352, -3513181, 1235728, -2434439, -266997,
            3562462, 2446433, -2244091, 3342478, -3817976, -2316500, -3407706, -2091667,

            -3839961, -3839961, 3628969, 3628969, 3881060, 3881060, 3019102, 3019102,
            1439742, 1439742, 812732, 812732, 1584928, 1584928, -1285669, -1285669,
            -1341330, - 1341330, -1315589, -1315589, 177440, 177440, 2409325, 2409325,
            1851402, 1851402, -3159746, -3159746, 3553272, 3553272, -189548, -189548,
            1316856, 1316856, -759969, -759969, 210977, 210977, -2389356, -2389356,
            3249728, 3249728, -1653064, -1653064, 8578, 8578, 3724342, 3724342,
            -3958618, -3958618, -904516, -904516, 1100098, 1100098, -44288, -44288,
            -3097992, -3097992, -508951, -508951, -264944, -264944, 3343383, 3343383,
            1430430, 1430430, -1852771, -1852771, -1349076, -1349076, 381987, 381987,
            1308169, 1308169, 22981, 22981, 1228525, 1228525, 671102, 671102,
            2477047, 2477047, 411027, 411027, 3693493, 3693493, 2967645, 2967645,
            -2715295, -2715295, -2147896, -2147896, 983419, 983419, -3412210, -3412210,
            -126922, -126922, 3632928, 3632928, 3157330, 3157330, 3190144, 3190144,
            1000202, 1000202, 4083598, 4083598, -1939314, -1939314, 1257611, 1257611,
            1585221, 1585221, -2176455, -2176455, -3475950, -3475950, 1452451, 1452451,
            3041255, 3041255, 3677745, 3677745, 1528703, 1528703, 3930395, 3930395,

            2797779, 2797779, 2797779, 2797779, -2071892, -2071892, -2071892, -2071892,
            2556880, 2556880, 2556880, 2556880, -3900724, -3900724, -3900724, -3900724,
            -3881043, -3881043, -3881043, -3881043, -954230, -954230, -954230, -954230,
            -531354, -531354, -531354, -531354, -811944, -811944, -811944, -811944,
            -3699596, -3699596, -3699596, -3699596, 1600420, 1600420, 1600420, 1600420,
            2140649, 2140649, 2140649, 2140649, -3507263, -3507263, -3507263, -3507263,
            3821735, 3821735, 3821735, 3821735, -3505694, -3505694, -3505694, -3505694,
            1643818, 1643818, 1643818, 1643818, 1699267, 1699267, 1699267, 1699267,
            539299, 539299, 539299, 539299, -2348700, -2348700, -2348700, -2348700,
            300467, 300467, 300467, 300467, -3539968, -3539968, -3539968, -3539968,
            2867647, 2867647, 2867647, 2867647, -3574422, -3574422, -3574422, -3574422,
            3043716, 3043716, 3043716, 3043716, 3861115, 3861115, 3861115, 3861115,
            -3915439, -3915439, -3915439, -3915439, 2537516, 2537516, 2537516, 2537516,
            3592148, 3592148, 3592148, 3592148, 1661693, 1661693, 1661693, 1661693,
            -3530437, -3530437, -3530437, -3530437, -3077325, -3077325, -3077325, -3077325,
            -95776, -95776, -95776, -95776, -2706023, -2706023, -2706023, -2706023,

            -280005, -280005, -280005, -280005, -280005, -280005, -280005, -280005,
            -4010497, -4010497, -4010497, -4010497, -4010497, -4010497, -4010497, -4010497,
            19422, 19422, 19422, 19422, 19422, 19422, 19422, 19422,
            -1757237, -1757237, -1757237, -1757237, -1757237, -1757237, -1757237, -1757237,
            3277672, 3277672, 3277672, 3277672, 3277672, 3277672, 3277672, 3277672,
            1399561, 1399561, 1399561, 1399561, 1399561, 1399561, 1399561, 1399561,
            3859737, 3859737, 3859737, 3859737, 3859737, 3859737, 3859737, 3859737,
            2118186, 2118186, 2118186, 2118186, 2118186, 2118186, 2118186, 2118186,
            2108549, 2108549, 2108549, 2108549, 2108549, 2108549, 2108549, 2108549,
            -2619752, -2619752, -2619752, -2619752, -2619752, -2619752, -2619752, -2619752,
            1119584, 1119584, 1119584, 1119584, 1119584, 1119584, 1119584, 1119584,
            549488, 549488, 549488, 549488, 549488, 549488, 549488, 549488,
            -3585928, -3585928, -3585928, -3585928, -3585928, -3585928, -3585928, -3585928,
            1079900, 1079900, 1079900, 1079900, 1079900, 1079900, 1079900, 1079900,
            -1024112, -1024112, -1024112, -1024112, -1024112, -1024112, -1024112, -1024112,
            -2725464, -2725464, -2725464, -2725464, -2725464, -2725464, -2725464, -2725464,

            -2680103, -2680103, -2680103, -2680103, -2680103, -2680103, -2680103, -2680103,
            -2680103, -2680103, -2680103, -2680103, -2680103, -2680103, -2680103, -2680103,
            -3111497, -3111497, -3111497, -3111497, -3111497, -3111497, -3111497, -3111497,
            -3111497, -3111497, -3111497, -3111497, -3111497, -3111497, -3111497, -3111497,
            2884855, 2884855, 2884855, 2884855, 2884855, 2884855, 2884855, 2884855,
            2884855, 2884855, 2884855, 2884855, 2884855, 2884855, 2884855, 2884855,
            -3119733, -3119733, -3119733, -3119733, -3119733, -3119733, -3119733, -3119733,
            -3119733, -3119733, -3119733, -3119733, -3119733, -3119733, -3119733, -3119733,
            2091905, 2091905, 2091905, 2091905, 2091905, 2091905, 2091905, 2091905,
            2091905, 2091905, 2091905, 2091905, 2091905, 2091905, 2091905, 2091905,
            359251, 359251, 359251, 359251, 359251, 359251, 359251, 359251,
            359251, 359251, 359251, 359251, 359251, 359251, 359251, 359251,
            -2353451, -2353451, -2353451, -2353451, -2353451, -2353451, -2353451, -2353451,
            -2353451, -2353451, -2353451, -2353451, -2353451, -2353451, -2353451, -2353451,
            -1826347, -1826347, -1826347, -1826347, -1826347, -1826347, -1826347, -1826347,
            -1826347, -1826347, -1826347, -1826347, -1826347, -1826347, -1826347, -1826347,

            -466468, -466468, -466468, -466468, -466468, -466468, -466468, -466468,
            -466468, -466468, -466468, -466468, -466468, -466468, -466468, -466468,
            -466468, -466468, -466468, -466468, -466468, -466468, -466468, -466468,
            -466468, -466468, -466468, -466468, -466468, -466468, -466468, -466468,
            876248, 876248, 876248, 876248, 876248, 876248, 876248, 876248,
            876248, 876248, 876248, 876248, 876248, 876248, 876248, 876248,
            876248, 876248, 876248, 876248, 876248, 876248, 876248, 876248,
            876248, 876248, 876248, 876248, 876248, 876248, 876248, 876248,
            777960, 777960, 777960, 777960, 777960, 777960, 777960, 777960,
            777960, 777960, 777960, 777960, 777960, 777960, 777960, 777960,
            777960, 777960, 777960, 777960, 777960, 777960, 777960, 777960,
            777960, 777960, 777960, 777960, 777960, 777960, 777960, 777960,
            -237124, -237124, -237124, -237124, -237124, -237124, -237124, -237124,
            -237124, -237124, -237124, -237124, -237124, -237124, -237124, -237124,
            -237124, -237124, -237124, -237124, -237124, -237124, -237124, -237124,
            -237124, -237124, -237124, -237124, -237124, -237124, -237124, -237124,

            518909, 518909, 518909, 518909, 518909, 518909, 518909, 518909,
            518909, 518909, 518909, 518909, 518909, 518909, 518909, 518909,
            518909, 518909, 518909, 518909, 518909, 518909, 518909, 518909,
            518909, 518909, 518909, 518909, 518909, 518909, 518909, 518909,
            518909, 518909, 518909, 518909, 518909, 518909, 518909, 518909,
            518909, 518909, 518909, 518909, 518909, 518909, 518909, 518909,
            518909, 518909, 518909, 518909, 518909, 518909, 518909, 518909,
            518909, 518909, 518909, 518909, 518909, 518909, 518909, 518909,
            2608894, 2608894, 2608894, 2608894, 2608894, 2608894, 2608894, 2608894,
            2608894, 2608894, 2608894, 2608894, 2608894, 2608894, 2608894, 2608894,
            2608894, 2608894, 2608894, 2608894, 2608894, 2608894, 2608894, 2608894,
            2608894, 2608894, 2608894, 2608894, 2608894, 2608894, 2608894, 2608894,
            2608894, 2608894, 2608894, 2608894, 2608894, 2608894, 2608894, 2608894,
            2608894, 2608894, 2608894, 2608894, 2608894, 2608894, 2608894, 2608894,
            2608894, 2608894, 2608894, 2608894, 2608894, 2608894, 2608894, 2608894,
            2608894, 2608894, 2608894, 2608894, 2608894, 2608894, 2608894, 2608894,

            -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847,
            -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847,
            -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847,
            -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847,
            -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847,
            -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847,
            -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847,
            -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847,
            -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847,
            -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847,
            -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847,
            -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847,
            -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847,
            -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847,
            -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847,
            -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847
    };

    private static final int[] MONT_ZETAS_FOR_VECTOR_NTT = new int[]{
            25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847,
            25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847,
            25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847,
            25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847,
            25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847,
            25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847,
            25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847,
            25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847,
            25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847,
            25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847,
            25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847,
            25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847,
            25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847,
            25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847,
            25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847,
            25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847,

            -2608894, -2608894, -2608894, -2608894, -2608894, -2608894, -2608894, -2608894,
            -2608894, -2608894, -2608894, -2608894, -2608894, -2608894, -2608894, -2608894,
            -2608894, -2608894, -2608894, -2608894, -2608894, -2608894, -2608894, -2608894,
            -2608894, -2608894, -2608894, -2608894, -2608894, -2608894, -2608894, -2608894,
            -2608894, -2608894, -2608894, -2608894, -2608894, -2608894, -2608894, -2608894,
            -2608894, -2608894, -2608894, -2608894, -2608894, -2608894, -2608894, -2608894,
            -2608894, -2608894, -2608894, -2608894, -2608894, -2608894, -2608894, -2608894,
            -2608894, -2608894, -2608894, -2608894, -2608894, -2608894, -2608894, -2608894,
            -518909, -518909, -518909, -518909, -518909, -518909, -518909, -518909,
            -518909, -518909, -518909, -518909, -518909, -518909, -518909, -518909,
            -518909, -518909, -518909, -518909, -518909, -518909, -518909, -518909,
            -518909, -518909, -518909, -518909, -518909, -518909, -518909, -518909,
            -518909, -518909, -518909, -518909, -518909, -518909, -518909, -518909,
            -518909, -518909, -518909, -518909, -518909, -518909, -518909, -518909,
            -518909, -518909, -518909, -518909, -518909, -518909, -518909, -518909,
            -518909, -518909, -518909, -518909, -518909, -518909, -518909, -518909,

            237124, 237124, 237124, 237124, 237124, 237124, 237124, 237124,
            237124, 237124, 237124, 237124, 237124, 237124, 237124, 237124,
            237124, 237124, 237124, 237124, 237124, 237124, 237124, 237124,
            237124, 237124, 237124, 237124, 237124, 237124, 237124, 237124,
            -777960, -777960, -777960, -777960, -777960, -777960, -777960, -777960,
            -777960, -777960, -777960, -777960, -777960, -777960, -777960, -777960,
            -777960, -777960, -777960, -777960, -777960, -777960, -777960, -777960,
            -777960, -777960, -777960, -777960, -777960, -777960, -777960, -777960,
            -876248, -876248, -876248, -876248, -876248, -876248, -876248, -876248,
            -876248, -876248, -876248, -876248, -876248, -876248, -876248, -876248,
            -876248, -876248, -876248, -876248, -876248, -876248, -876248, -876248,
            -876248, -876248, -876248, -876248, -876248, -876248, -876248, -876248,
            466468, 466468, 466468, 466468, 466468, 466468, 466468, 466468,
            466468, 466468, 466468, 466468, 466468, 466468, 466468, 466468,
            466468, 466468, 466468, 466468, 466468, 466468, 466468, 466468,
            466468, 466468, 466468, 466468, 466468, 466468, 466468, 466468,

            1826347, 1826347, 1826347, 1826347, 1826347, 1826347, 1826347, 1826347,
            1826347, 1826347, 1826347, 1826347, 1826347, 1826347, 1826347, 1826347,
            2353451, 2353451, 2353451, 2353451, 2353451, 2353451, 2353451, 2353451,
            2353451, 2353451, 2353451, 2353451, 2353451, 2353451, 2353451, 2353451,
            -359251, -359251, -359251, -359251, -359251, -359251, -359251, -359251,
            -359251, -359251, -359251, -359251, -359251, -359251, -359251, -359251,
            -2091905, -2091905, -2091905, -2091905, -2091905, -2091905, -2091905, -2091905,
            -2091905, -2091905, -2091905, -2091905, -2091905, -2091905, -2091905, -2091905,
            3119733, 3119733, 3119733, 3119733, 3119733, 3119733, 3119733, 3119733,
            3119733, 3119733, 3119733, 3119733, 3119733, 3119733, 3119733, 3119733,
            -2884855, -2884855, -2884855, -2884855, -2884855, -2884855, -2884855, -2884855,
            -2884855, -2884855, -2884855, -2884855, -2884855, -2884855, -2884855, -2884855,
            3111497, 3111497, 3111497, 3111497, 3111497, 3111497, 3111497, 3111497,
            3111497, 3111497, 3111497, 3111497, 3111497, 3111497, 3111497, 3111497,
            2680103, 2680103, 2680103, 2680103, 2680103, 2680103, 2680103, 2680103,
            2680103, 2680103, 2680103, 2680103, 2680103, 2680103, 2680103, 2680103,

            2725464, 2725464, 2725464, 2725464, 2725464, 2725464, 2725464, 2725464,
            1024112, 1024112, 1024112, 1024112, 1024112, 1024112, 1024112, 1024112,
            -1079900, -1079900, -1079900, -1079900, -1079900, -1079900, -1079900, -1079900,
            3585928, 3585928, 3585928, 3585928, 3585928, 3585928, 3585928, 3585928,
            -549488, -549488, -549488, -549488, -549488, -549488, -549488, -549488,
            -1119584, -1119584, -1119584, -1119584, -1119584, -1119584, -1119584, -1119584,
            2619752, 2619752, 2619752, 2619752, 2619752, 2619752, 2619752, 2619752,
            -2108549, -2108549, -2108549, -2108549, -2108549, -2108549, -2108549, -2108549,
            -2118186, -2118186, -2118186, -2118186, -2118186, -2118186, -2118186, -2118186,
            -3859737, -3859737, -3859737, -3859737, -3859737, -3859737, -3859737, -3859737,
            -1399561, -1399561, -1399561, -1399561, -1399561, -1399561, -1399561, -1399561,
            -3277672, -3277672, -3277672, -3277672, -3277672, -3277672, -3277672, -3277672,
            1757237, 1757237, 1757237, 1757237, 1757237, 1757237, 1757237, 1757237,
            -19422, -19422, -19422, -19422, -19422, -19422, -19422, -19422,
            4010497, 4010497, 4010497, 4010497, 4010497, 4010497, 4010497, 4010497,
            280005, 280005, 280005, 280005, 280005, 280005, 280005, 280005,

            2706023, 2706023, 2706023, 2706023, 95776, 95776, 95776, 95776,
            3077325, 3077325, 3077325, 3077325, 3530437, 3530437, 3530437, 3530437,
            -1661693, -1661693, -1661693, -1661693, -3592148, -3592148, -3592148, -3592148,
            -2537516, -2537516, -2537516, -2537516, 3915439, 3915439, 3915439, 3915439,
            -3861115, -3861115, -3861115, -3861115, -3043716, -3043716, -3043716, -3043716,
            3574422, 3574422, 3574422, 3574422, -2867647, -2867647, -2867647, -2867647,
            3539968, 3539968, 3539968, 3539968, -300467, -300467, -300467, -300467,
            2348700, 2348700, 2348700, 2348700, -539299, -539299, -539299, -539299,
            -1699267, -1699267, -1699267, -1699267, -1643818, -1643818, -1643818, -1643818,
            3505694, 3505694, 3505694, 3505694, -3821735, -3821735, -3821735, -3821735,
            3507263, 3507263, 3507263, 3507263, -2140649, -2140649, -2140649, -2140649,
            -1600420, -1600420, -1600420, -1600420, 3699596, 3699596, 3699596, 3699596,
            811944, 811944, 811944, 811944, 531354, 531354, 531354, 531354,
            954230, 954230, 954230, 954230, 3881043, 3881043, 3881043, 3881043,
            3900724, 3900724, 3900724, 3900724, -2556880, -2556880, -2556880, -2556880,
            2071892, 2071892, 2071892, 2071892, -2797779, -2797779, -2797779, -2797779,

            -3930395, -3930395, -1528703, -1528703, -3677745, -3677745, -3041255, -3041255,
            -1452451, -1452451, 3475950, 3475950, 2176455, 2176455, -1585221, -1585221,
            -1257611, -1257611, 1939314, 1939314, -4083598, -4083598, -1000202, -1000202,
            -3190144, -3190144, -3157330, -3157330, -3632928, -3632928, 126922, 126922,
            3412210, 3412210, -983419, -983419, 2147896, 2147896, 2715295, 2715295,
            -2967645, -2967645, -3693493, -3693493, -411027, -411027, -2477047, -2477047,
            -671102, -671102, -1228525, -1228525, -22981, -22981, -1308169, -1308169,
            -381987, -381987, 1349076, 1349076, 1852771, 1852771, -1430430, -1430430,
            -3343383, -3343383, 264944, 264944, 508951, 508951, 3097992, 3097992,
            44288, 44288, -1100098, -1100098, 904516, 904516, 3958618, 3958618,
            -3724342, -3724342, -8578, -8578, 1653064, 1653064, -3249728, -3249728,
            2389356, 2389356, -210977, -210977, 759969, 759969, -1316856, -1316856,
            189548, 189548, -3553272, -3553272, 3159746, 3159746, -1851402, -1851402,
            -2409325, -2409325, -177440, -177440, 1315589, 1315589, 1341330, 1341330,
            1285669, 1285669, -1584928, -1584928, -812732, -812732, -1439742, -1439742,
            -3019102, -3019102, -3881060, -3881060, -3628969, -3628969, 3839961, 3839961,

            2091667, 3407706, 2316500, 3817976, -3342478, 2244091, -2446433, -3562462,
            266997, 2434439, -1235728, 3513181, -3520352, -3759364, -1197226, -3193378,
            900702, 1859098, 909542, 819034, 495491, -1613174, -43260, -522500,
            -655327, -3122442, 2031748, 3207046, -3556995, -525098, -768622, -3595838,
            342297, 286988, -2437823, 4108315, 3437287, -3342277, 1735879, 203044,
            2842341, 2691481, -2590150, 1265009, 4055324, 1247620, 2486353, 1595974,
            -3767016, 1250494, 2635921, -3548272, -2994039, 1869119, 1903435, -1050970,
            -1333058, 1237275, -3318210, -1430225, -451100, 1312455, 3306115, -1962642,
            -1279661, 1917081, -2546312, -1374803, 1500165, 777191, 2235880, 3406031,
            -542412, -2831860, -1671176, -1846953, -2584293, -3724270, 594136, -3776993,
            -2013608, 2432395, 2454455, -164721, 1957272, 3369112, 185531, -1207385,
            -3183426, 162844, 1616392, 3014001, 810149, 1652634, -3694233, -1799107,
            -3038916, 3523897, 3866901, 269760, 2213111, -975884, 1717735, 472078,
            -426683, 1723600, -1803090, 1910376, -1667432, -1104333, -260646, -3833893,
            -2939036, -2235985, -420899, -2286327, 183443, -976891, 1612842, -3545687,
            -554416, 3919660, -48306, -1362209, 3937738, 1400424, -846154, 1976782
    };
}
